/**************************************************************************
// nAsterisk - .NET Asterisk Library 
//
// Copyright (c) 2007 by:
// Fabio Cavalcante (fabio(a)codesapien.com)
// Josh Perry (josh(a)6bit.com)
// Justin Long (dukk(a)dukk.org)
//
// Asterisk - Copyright (C) 1999 - 2006, Digium, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published 
// by  the Free Software Foundation; either version 2 of the License or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this package (See COPYING.LIB); if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/**************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using nAsterisk.Configuration;

namespace nAsterisk.AGI
{
	public class TcpAGIScriptHost : IAGIScriptHost
	{
		#region MemberVars
		TcpListener _listener;
		Dictionary<string, Type> _mappings = new Dictionary<string,Type>();
		#endregion

		#region Constructors
		public TcpAGIScriptHost()
			: this(4573)
		{

		}

		public TcpAGIScriptHost(int port) : this(IPAddress.Any, port)
		{

		}

		public TcpAGIScriptHost(IPAddress addr, int port) : this(new IPEndPoint(addr, port))
		{

		}

		public TcpAGIScriptHost(IPEndPoint localEP)
		{
			_listener = new TcpListener(localEP);
		}
		#endregion

        protected Dictionary<string, Type> Mappings
        {
            get { return _mappings; }
        }

		/// <summary>
		/// Gets configuration from the config source
		/// </summary>
		public virtual void Configure(ITcpHostConfigurationSource config)
		{
			_mappings = config.GetUriMappings();
		}

		public virtual void AddUri(string uri, Type scriptType)
		{
			if (_mappings.ContainsKey(uri))
				throw new ArgumentException("specified uri is already mapped to a script", "uri");

			if (scriptType.IsSubclassOf(typeof(IAsteriskAGIScript)))
				_mappings.Add(uri, scriptType);
			else
				throw new ArgumentException("script type must implement IAsteriskAGIScript", "scriptType");

			_mappings.Add(uri, scriptType);
		}

		/// <summary>
		/// Async target for _listener.BeginAcceptTcpClient
		/// </summary>
		/// <param name="iar"></param>
		private void onConnectionEstabilished(IAsyncResult iar)
		{
			// End the accept
			TcpClient client = null;

			try
			{
				if (iar.IsCompleted)
					client = _listener.EndAcceptTcpClient(iar);
			}
			catch (ObjectDisposedException) { }
			
			if (client != null)
			{
				this.DispatchScript(client);

				// Restart the Accept
				_listener.BeginAcceptTcpClient(new AsyncCallback(onConnectionEstabilished), null);
			}
		}

		protected virtual void DispatchScript(TcpClient client)
		{
			System.Threading.Thread t = new System.Threading.Thread(delegate()
			{
				using (Stream stream = client.GetStream())
				{
                    StreamAGIChannel agiChannel = new StreamAGIChannel(stream);

					AsteriskAGI agi = new AsteriskAGI(agiChannel);
					agi.Init();

					Uri uri = new Uri(agi.RequestInfo.Request);

					// Parse the query string variables
					Dictionary<string, string> vars;
					if (!string.IsNullOrEmpty(uri.Query))
					{
						// Get rid of the ? and split on &
						string[] qvars = uri.Query.Substring(1).Split('&');
						vars = new Dictionary<string, string>(qvars.Length);
						Array.ForEach<string>(qvars, delegate(string qvar)
						{
							string[] var = qvar.Split('=');
							vars.Add(var[0], var[1]);
						});
					}
					else
						vars = new Dictionary<string, string>();

					// Look at the incoming URL and match it to a script
					if (_mappings.ContainsKey(uri.AbsolutePath))
					{
						Type scriptType = _mappings[uri.AbsolutePath];
						IAsteriskAGIScript script = (IAsteriskAGIScript)Activator.CreateInstance(scriptType);
						script.Execute(agi, vars);

						System.Threading.Thread.CurrentThread.Name = string.Format("Agi script {0}", scriptType.Name);
					}
				}

				client.Close();
			});
			
			t.Start();
		}

		#region IAGIScriptHost Members

		public virtual void Start()
		{
			_listener.Start();
			_listener.BeginAcceptTcpClient(new AsyncCallback(onConnectionEstabilished), null);
		}

		public virtual void Stop()
		{	
			_listener.Stop();
		}

		#endregion
	}
}
